Option Explicit

' ======================================================================
' 执行所有测试用例
' ======================================================================
Public Sub test_all_case()
    ' 测试: 获取当前解析结果的类型
    test_getType
    ' 测试: 解析开始部分的作用域
    test_parseStartCompositionEnv
    ' 测试: 解析sql的各个组成部分
    test_parseSubCompositionEnv
End Sub

' ======================================================================
' 测试通用处理
' ======================================================================

' 创建一个格式化器的 sql 解析器
Private Function createSqlFormatterAnalyzer() As SqlFormatterAnalyzer
    Dim config As SqlFormatterConfiguration
    Set config = New SqlFormatterConfiguration

    Dim result As SqlFormatterAnalyzer
    Set result = New SqlFormatterAnalyzer

    result.Init config

    Set createSqlFormatterAnalyzer = result
End Function

' 创建一个扫描器
Private Function getObjArrayRefScanner(ParamArray tokens() As Variant) As ObjArrayRefScanner
    Dim i As Integer
    Dim t As SqlToken

    Dim tokenCount As Integer
    tokenCount = UBound(tokens)

    ' 创建一个和参数数量相同的数组引用
    Dim meanTokens As IObjArrayRef
    Set meanTokens = UtilsArrayRef.createRealObjArrayRef(tokenCount)

    ' 遍历参数，并保持到数组引用
    For i = 0 To UBound(tokens)
        Set t = tokens(i)

        meanTokens.Add t
    Next

    ' 创建扫描器
    Dim scanner As ObjArrayRefScanner
    Set scanner = New ObjArrayRefScanner
    scanner.Init meanTokens

    ' return scanner
    Set getObjArrayRefScanner = scanner
End Function

' 向一个数组引用中添加元素
Private Sub addElemToArray(ByRef arr As IObjArrayRef, ParamArray tokens() As Variant)
    Dim i As Integer
    Dim t As SqlToken

    Dim tokenCount As Integer
    tokenCount = UBound(tokens)

    ' 遍历参数，并保持到数组引用
    For i = 0 To UBound(tokens)
        Set t = tokens(i)

        arr.Add t
    Next
End Sub

' 获取一个 [;] 元素
Private Function getSemicolonToken() As SqlToken
    Set getSemicolonToken = New SqlToken
    getSemicolonToken.Init ";", SqlTokenType.SEMICOLON
End Function


' 获取一个 recursive 元素
Private Function getRecursiveToken() As SqlToken
    Set getRecursiveToken = New SqlToken
    getRecursiveToken.Init "recursive", SqlTokenType.KEY_RECURSIVE
End Function

' 获取一个 from 元素
Private Function getFromToken() As SqlToken
    Set getFromToken = New SqlToken
    getFromToken.Init "from", SqlTokenType.KEY_FROM
End Function

' 获取一个 cast 元素
Private Function getCastToken() As SqlToken
    Set getCastToken = New SqlToken
    getCastToken.Init "cast", SqlTokenType.KEY_CAST
End Function

' 创建一个字符串 token
Private Function createStrToken(ByVal v As String) As SqlToken
    Set createStrToken = New SqlToken
    createStrToken.Init "'" & v & "'", SqlTokenType.STR_STR
End Function

' where 关键字
Private Function getWhereToken() As SqlToken
    Set getWhereToken = New SqlToken
    getWhereToken.Init "where", SqlTokenType.KEY_WHERE
End Function

' having 关键字
Private Function getHavingToken() As SqlToken
    Set getHavingToken = New SqlToken
    getHavingToken.Init "having", SqlTokenType.KEY_HAVING
End Function

' on 关键字
Private Function getOnToken() As SqlToken
    Set getOnToken = New SqlToken
    getOnToken.Init "on", SqlTokenType.KEY_ON
End Function

' case 运算符
Private Function getCaseToken() As SqlToken
    Set getCaseToken = New SqlToken
    getCaseToken.Init "case", SqlTokenType.KEY_CASE
End Function

' when 运算符
Private Function getWhenToken() As SqlToken
    Set getWhenToken = New SqlToken
    getWhenToken.Init "when", SqlTokenType.KEY_WHEN
End Function

' then 运算符
Private Function getThenToken() As SqlToken
    Set getThenToken = New SqlToken
    getThenToken.Init "then", SqlTokenType.KEY_THEN
End Function

' else 运算符
Private Function getElseToken() As SqlToken
    Set getElseToken = New SqlToken
    getElseToken.Init "else", SqlTokenType.KEY_ELSE
End Function

' end 运算符
Private Function getEndToken() As SqlToken
    Set getEndToken = New SqlToken
    getEndToken.Init "end", SqlTokenType.KEY_END
End Function

' ASC 运算符
Private Function getAscToken() As SqlToken
    Set getAscToken = New SqlToken
    getAscToken.Init "asc", SqlTokenType.KEY_ASC
End Function

' DESC 运算符
Private Function getDescToken() As SqlToken
    Set getDescToken = New SqlToken
    getDescToken.Init "desc", SqlTokenType.KEY_DESC
End Function

' || 运算符
Private Function getStrConcatToken() As SqlToken
    Set getStrConcatToken = New SqlToken
    getStrConcatToken.Init "||", SqlTokenType.OPERA_STR_CONCAT
End Function

' + 运算符
Private Function getPlusToken() As SqlToken
    Set getPlusToken = New SqlToken
    getPlusToken.Init "+", SqlTokenType.OPERA_PLUS
End Function

' - 运算符
Private Function getMinusToken() As SqlToken
    Set getMinusToken = New SqlToken
    getMinusToken.Init "-", SqlTokenType.OPERA_MINUS
End Function

' 创建一个数值元素
Private Function createNumToken(ByVal v As String) As SqlToken
    Set createNumToken = New SqlToken
    createNumToken.Init v, SqlTokenType.STR_NUM
End Function

' 获取一个 true 元素
Private Function getTrueToken() As SqlToken
    Set getTrueToken = New SqlToken
    getTrueToken.Init "true", SqlTokenType.KEY_TRUE
End Function

' 获取一个 false 元素
Private Function getFalseToken() As SqlToken
    Set getFalseToken = New SqlToken
    getFalseToken.Init "false", SqlTokenType.KEY_FALSE
End Function

' 获取一个 null 元素
Private Function getNullToken() As SqlToken
    Set getNullToken = New SqlToken
    getNullToken.Init "null", SqlTokenType.KEY_NULL
End Function

' 获取 * 元素
Private Function getAsteriskToken() As SqlToken
    Set getAsteriskToken = New SqlToken
    getAsteriskToken.Init "*", SqlTokenType.ASTERISK
End Function

' 获取 binary 关键字
Private Function getBinaryToken() As SqlToken
    Set getBinaryToken = New SqlToken
    getBinaryToken.Init "binary", SqlTokenType.KEY_BINARY
End Function

' 获取 order by 关键字
Private Function getOrderByToken() As SqlToken
    Set getOrderByToken = New SqlToken
    getOrderByToken.Init "order by", SqlTokenType.KEY_ORDER_BY
End Function

' 获取 group by 关键字
Private Function getGroupByToken() As SqlToken
    Set getGroupByToken = New SqlToken
    getGroupByToken.Init "group by", SqlTokenType.KEY_GROUP_BY
End Function

' 获取 with 关键字
Private Function getWithToken() As SqlToken
    Set getWithToken = New SqlToken
    getWithToken.Init "with", SqlTokenType.KEY_WITH
End Function

' 获取 ROLLUP 关键字
Private Function getRollupToken() As SqlToken
    Set getRollupToken = New SqlToken
    getRollupToken.Init "rollup", SqlTokenType.KEY_ROLLUP
End Function

' 获取 offset 关键字
Private Function getOffsetToken() As SqlToken
    Set getOffsetToken = New SqlToken
    getOffsetToken.Init "offset", SqlTokenType.KEY_OFFSET
End Function

' 获取 limit 关键字
Private Function getLimitToken() As SqlToken
    Set getLimitToken = New SqlToken
    getLimitToken.Init "limit", SqlTokenType.KEY_LIMIT
End Function

' 获取 is 关键字
Private Function getIsToken() As SqlToken
    Set getIsToken = New SqlToken
    getIsToken.Init "is", SqlTokenType.KEY_IS
End Function

' 获取 not 关键字
Private Function getNotToken() As SqlToken
    Set getNotToken = New SqlToken
    getNotToken.Init "not", SqlTokenType.KEY_NOT
End Function

' 获取 union 关键字
Private Function getUnionToken() As SqlToken
    Set getUnionToken = New SqlToken
    getUnionToken.Init "union", SqlTokenType.KEY_UNION
End Function

' 获取 select 关键字
Private Function getSelectToken() As SqlToken
    Set getSelectToken = New SqlToken
    getSelectToken.Init "select", SqlTokenType.KEY_SELECT
End Function

' 获取 distinct 关键字
Private Function getDistinctToken() As SqlToken
    Set getDistinctToken = New SqlToken
    getDistinctToken.Init "distinct", SqlTokenType.KEY_DISTINCT
End Function

' 获取 all 关键字
Private Function getAllToken() As SqlToken
    Set getAllToken = New SqlToken
    getAllToken.Init "all", SqlTokenType.KEY_ALL
End Function

' 获取 join 关键字
Private Function getJoinToken() As SqlToken
    Set getJoinToken = New SqlToken
    getJoinToken.Init "join", SqlTokenType.KEY_JOIN
End Function

' 获取 left 关键字
Private Function getLeftToken() As SqlToken
    Set getLeftToken = New SqlToken
    getLeftToken.Init "left", SqlTokenType.KEY_LEFT
End Function

' 获取 right 关键字
Private Function getRightToken() As SqlToken
    Set getRightToken = New SqlToken
    getRightToken.Init "right", SqlTokenType.KEY_RIGHT
End Function

' 获取 inner 关键字
Private Function getInnerToken() As SqlToken
    Set getInnerToken = New SqlToken
    getInnerToken.Init "inner", SqlTokenType.KEY_INNER
End Function

' 获取 cross 关键字
Private Function getCrossToken() As SqlToken
    Set getCrossToken = New SqlToken
    getCrossToken.Init "cross", SqlTokenType.KEY_CROSS
End Function

' 获取 out 关键字
Private Function getOuterToken() As SqlToken
    Set getOuterToken = New SqlToken
    getOuterToken.Init "outer", SqlTokenType.KEY_OUTER
End Function

' 获取 use 关键字
Private Function getUseToken() As SqlToken
    Set getUseToken = New SqlToken
    getUseToken.Init "use", SqlTokenType.KEY_USE
End Function

' 获取 index 关键字
Private Function getIndexToken() As SqlToken
    Set getIndexToken = New SqlToken
    getIndexToken.Init "index", SqlTokenType.KEY_INDEX
End Function

' 获取 ignore 关键字
Private Function getIgnoreToken() As SqlToken
    Set getIgnoreToken = New SqlToken
    getIgnoreToken.Init "ignore", SqlTokenType.KEY_IGNORE
End Function

' 获取 key 关键字
Private Function getKeyToken() As SqlToken
    Set getKeyToken = New SqlToken
    getKeyToken.Init "key", SqlTokenType.KEY_KEY
End Function

' 获取 for 关键字
Private Function getForToken() As SqlToken
    Set getForToken = New SqlToken
    getForToken.Init "for", SqlTokenType.KEY_FOR
End Function

' 获取 force 关键字
Private Function getForceToken() As SqlToken
    Set getForceToken = New SqlToken
    getForceToken.Init "force", SqlTokenType.KEY_FORCE
End Function

' 获取 as 关键字
Private Function getAsToken() As SqlToken
    Set getAsToken = New SqlToken
    getAsToken.Init "as", SqlTokenType.KEY_AS
End Function

' 获取左括号
Private Function getLeftParenthesisToken() As SqlToken
    Set getLeftParenthesisToken = New SqlToken
    getLeftParenthesisToken.Init "(", SqlTokenType.LEFT_PARENTHESIS
End Function

' 获取右括号
Private Function getRightParenthesisToken() As SqlToken
    Set getRightParenthesisToken = New SqlToken
    getRightParenthesisToken.Init ")", SqlTokenType.RIGHT_PARENTHESIS
End Function

' 获取 partition 关键字
Private Function getPartitionToken() As SqlToken
    Set getPartitionToken = New SqlToken
    getPartitionToken.Init "partition", SqlTokenType.KEY_PARTITION
End Function

' 获取逻辑运算符: and
Private Function getAndToken() As SqlToken
    Set getAndToken = New SqlToken
    getAndToken.Init "and", SqlTokenType.KEY_AND
End Function

' 获取一个条件运算符: <=
Private Function getLeConditionOperator() As SqlToken
    Set getLeConditionOperator = New SqlToken
    getLeConditionOperator.Init "<=", SqlTokenType.OPERA_LE
End Function

' 获取一个任意名称的 STR_NAME token
Private Function getStrNameToken(ByRef s As String) As SqlToken
    Set getStrNameToken = New SqlToken
    getStrNameToken.Init s, SqlTokenType.STR_NAME
End Function

' 获取表名 token
Private Function getTableNameToken() As SqlToken
    Set getTableNameToken = New SqlToken
    getTableNameToken.Init TEST_TABLE_NAME, SqlTokenType.STR_NAME
End Function

' 获取 [.] token
Private Function getPointToken() As SqlToken
    Set getPointToken = New SqlToken
    getPointToken.Init TEST_POINT, SqlTokenType.POINT
End Function

' 获取列名 token
Private Function getColNameToken() As SqlToken
    Set getColNameToken = New SqlToken
    getColNameToken.Init TEST_COL_NAME, SqlTokenType.STR_NAME
End Function

' 创建和前一个元素在同一行的注释
Private Function createShareLineCommentToken(ByRef v As String) As SqlToken
    Set createShareLineCommentToken = New SqlToken
    createShareLineCommentToken.Init v, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function createSingleLineCommentToken(ByRef v As String) As SqlToken
    Set createSingleLineCommentToken = New SqlToken
    createSingleLineCommentToken.Init v, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取和前一个元素在同一行的注释
Private Function getShareLineCommentToken() As SqlToken
    Set getShareLineCommentToken = New SqlToken
    getShareLineCommentToken.Init TEST_SHARE_LINE_COMMENT, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function getSingleLineCommentToken() As SqlToken
    Set getSingleLineCommentToken = New SqlToken
    getSingleLineCommentToken.Init TEST_SINGLE_LINE_COMMENT, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取一个逗号的元素
Private Function getCommaToken() As SqlToken
    Set getCommaToken = New SqlToken
    getCommaToken.Init ",", SqlTokenType.COMMA
End Function

' 生成一个函数名称元素
Private Function createFuncNameToken(ByVal funcName As String) As SqlToken
    Set createFuncNameToken = New SqlToken
    createFuncNameToken.Init funcName, SqlTokenType.STR_FUNC
End Function

' 获取 CURRENT_TIMESTAMP 元素
Private Function getCurrentTimestampToken() As SqlToken
    Set getCurrentTimestampToken = New SqlToken
    getCurrentTimestampToken.Init "CURRENT_TIMESTAMP", SqlTokenType.CONST_CURRENT_TIMESTAMP
End Function

' 获取 exists 元素
Private Function getExistsToken() As SqlToken
    Set getExistsToken = New SqlToken
    getExistsToken.Init "exists", SqlTokenType.KEY_EXISTS
End Function


' 获取 insert 关键字
Private Function getInsertToken() As SqlToken
    Set getInsertToken = New SqlToken
    getInsertToken.Init "insert", SqlTokenType.KEY_INSERT
End Function

' 获取 into 关键字
Private Function getIntoToken() As SqlToken
    Set getIntoToken = New SqlToken
    getIntoToken.Init "into", SqlTokenType.KEY_INTO
End Function

' 获取 value 关键字
Private Function getValueToken() As SqlToken
    Set getValueToken = New SqlToken
    getValueToken.Init "value", SqlTokenType.KEY_VALUE
End Function

' 获取 values 关键字
Private Function getValuesToken() As SqlToken
    Set getValuesToken = New SqlToken
    getValuesToken.Init "values", SqlTokenType.KEY_VALUES
End Function

' 获取 HIGH_PRIORITY 关键字
Private Function getHighPriorityToken() As SqlToken
    Set getHighPriorityToken = New SqlToken
    getHighPriorityToken.Init "high_priority", SqlTokenType.KEY_HIGH_PRIORITY
End Function

' 获取 LOW_PRIORITY 关键字
Private Function getLowPriorityToken() As SqlToken
    Set getLowPriorityToken = New SqlToken
    getLowPriorityToken.Init "low_priority", SqlTokenType.KEY_LOW_PRIORITY
End Function

' 获取 delayed 关键字
Private Function getDelayedToken() As SqlToken
    Set getDelayedToken = New SqlToken
    getDelayedToken.Init "delayed", SqlTokenType.KEY_DELAYED
End Function

' 获取 set 关键字
Private Function getSetToken() As SqlToken
    Set getSetToken = New SqlToken
    getSetToken.Init "set", SqlTokenType.KEY_SET
End Function

' 获取一个条件运算符: =
Private Function getEqConditionOperator() As SqlToken
    Set getEqConditionOperator = New SqlToken
    getEqConditionOperator.Init "=", SqlTokenType.OPERA_EQ
End Function

' 获取 duplicate 关键字
Private Function getDuplicateToken() As SqlToken
    Set getDuplicateToken = New SqlToken
    getDuplicateToken.Init "duplicate", SqlTokenType.KEY_DUPLICATE
End Function

' 获取 update 关键字
Private Function getUpdateToken() As SqlToken
    Set getUpdateToken = New SqlToken
    getUpdateToken.Init "update", SqlTokenType.KEY_UPDATE
End Function

' ======================================================================
' 类方法的测试
' ======================================================================

' 测试: 获取当前解析结果的类型
Private Sub test_getType()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim insertAnalyzer As SqlFormatterInsertSubEAnalyzer
    Set insertAnalyzer = New SqlFormatterInsertSubEAnalyzer
    insertAnalyzer.Init analyzer

    Debug.Assert insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_getType = SqlTokenType.KEY_INSERT
End Sub

' 测试: 解析开始部分的作用域
' 参考： Test___SqlFormatterAnalyzer_analyze_env.bas.vb 下的的 parseInsertStartEnv
'      这里只测试一个方法，检查能不能返回解析结果对象
Private Sub test_parseStartCompositionEnv()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim insertAnalyzer As SqlFormatterInsertSubEAnalyzer
    Set insertAnalyzer = New SqlFormatterInsertSubEAnalyzer
    insertAnalyzer.Init analyzer

    Dim scanner As ObjArrayRefScanner
    Dim token As SqlToken
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内:  insert + 一个注释
    '                + low_priority + 一个同行注释 + 一个单行注释
    '                + ignore + 一个同行注释 + 一个单行注释
    '                + delayed + 一个同行注释 + 一个单行注释
    '                + high_priority + 一个同行注释 + 一个单行注释
    '                + 一个 UNION
    '    测试: 多个筛选关键字可以被吸收 + 多个筛选关键子之间的注释可以被吸收
    '          + 筛选关键字后的一个同行注释可以被吸收 + 筛选关键字和下一个作用域间的注释不会被吸收
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getInsertToken, createShareLineCommentToken("--c1"), _
        getLowPriorityToken, createShareLineCommentToken("--c2"), createSingleLineCommentToken("--c3"), _
        getIgnoreToken, createShareLineCommentToken("--c4"), createSingleLineCommentToken("--c5"), _
        getDelayedToken, createShareLineCommentToken("--c6"), createSingleLineCommentToken("--c7"), _
        getHighPriorityToken, createShareLineCommentToken("--c8"), createSingleLineCommentToken("--c9"), _
        getUnionToken)

    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseStartCompositionEnv(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.SINGLE_LINE_COMMENT
    Debug.Assert token.Value = "--c9"

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "insert --c1"
    Debug.Assert strArr.Item(1) = "    low_priority --c2"
    Debug.Assert strArr.Item(2) = "    --c3"
    Debug.Assert strArr.Item(3) = "    ignore --c4"
    Debug.Assert strArr.Item(4) = "    --c5"
    Debug.Assert strArr.Item(5) = "    delayed --c6"
    Debug.Assert strArr.Item(6) = "    --c7"
    Debug.Assert strArr.Item(7) = "    high_priority --c8"

End Sub

' 测试: 解析select的各个组成部分
Private Sub test_parseSubCompositionEnv()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim insertAnalyzer As SqlFormatterInsertSubEAnalyzer
    Set insertAnalyzer = New SqlFormatterInsertSubEAnalyzer
    insertAnalyzer.Init analyzer

    Dim scanner As ObjArrayRefScanner
    Dim token As SqlToken
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 1. 扫描器内: into + 一个注释
    '          + STR_NAME + 一个同行注释
    '         + (
    '             STR_NAEM, STR_NAME
    '         + ) + 一个同行注释
    '         + UNION
    '    测试: 解析 INTO
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getIntoToken, createShareLineCommentToken("--c1"), _
        _
        getStrNameToken("table1"), createShareLineCommentToken("--c2"), _
        _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getRightParenthesisToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_INTO)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.getType = SqlTokenType.KEY_INTO

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0) = "into --c1"
    Debug.Assert strArr.Item(1) = "    table1 --c2"
    Debug.Assert strArr.Item(2) = "    ("
    Debug.Assert strArr.Item(3) = "        x1,"
    Debug.Assert strArr.Item(4) = "        x2"
    Debug.Assert strArr.Item(5) = "    )"

    ' ==============================
    ' 2. 扫描器内: values + 一个同行注释
    '           + ( 'a1', 'a2' )
    '           + [,] + ( 'b1', 'b2' )
    '           + union
    '    测试: 解析 values
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getValuesToken, createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, createStrToken("a1"), getCommaToken, createStrToken("a2"), getRightParenthesisToken, _
        _
        getCommaToken, _
        _
        getLeftParenthesisToken, createStrToken("b1"), getCommaToken, createStrToken("b2"), getRightParenthesisToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_VALUES)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert elem.getType = SqlTokenType.KEY_VALUES

    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 9
    Debug.Assert strArr.Item(0) = "values --c1"
    Debug.Assert strArr.Item(1) = "    ("
    Debug.Assert strArr.Item(2) = "        'a1',"
    Debug.Assert strArr.Item(3) = "        'a2'"
    Debug.Assert strArr.Item(4) = "    ),"
    Debug.Assert strArr.Item(5) = "    ("
    Debug.Assert strArr.Item(6) = "        'b1',"
    Debug.Assert strArr.Item(7) = "        'b2'"
    Debug.Assert strArr.Item(8) = "    )"

    ' ==============================
    ' 3. 扫描器内: value + 一个同行注释
    '           + ( 'a1', 'a2' )
    '           + union
    '    测试: 解析 value
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getValueToken, createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, createStrToken("a1"), getCommaToken, createStrToken("a2"), getRightParenthesisToken, _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_VALUE)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert elem.getType = SqlTokenType.KEY_VALUE

    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 5
    Debug.Assert strArr.Item(0) = "value --c1"
    Debug.Assert strArr.Item(1) = "    ("
    Debug.Assert strArr.Item(2) = "        'a1',"
    Debug.Assert strArr.Item(3) = "        'a2'"
    Debug.Assert strArr.Item(4) = "    )"

    ' ==============================
    ' 4. 扫描器内:  as + 同行注释 + STR_FUNC
    '            + ( c1, c2, c3 ) + 同行注释
    '            + UNION
    '    测试: 解析 rowAlias
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getAsToken, createShareLineCommentToken("--c1"), _
        createFuncNameToken("newRow"), _
        _
        getLeftParenthesisToken, _
        getStrNameToken("c1"), getCommaToken, getStrNameToken("c2"), getCommaToken, getStrNameToken("c3"), _
        getRightParenthesisToken, createShareLineCommentToken("--c2"), _
        _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_AS)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert elem.getType = SqlTokenType.KEY_AS

    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "as --c1"
    Debug.Assert strArr.Item(1) = "    newRow (c1, c2, c3) --c2"

    ' ==============================
    ' 5. 扫描器内: set + 一个同行注释
    '    + STR_NAME + [=] + STR_NAME + [,]
    '    + STR_NAME + [=] + STR_NAME
    '    + UNION
    '    测试: 解析 set
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getSetToken, createShareLineCommentToken("--c1"), _
        getStrNameToken("x1"), getEqConditionOperator, getStrNameToken("x2"), _
        getCommaToken, _
        getStrNameToken("x3"), getEqConditionOperator, getStrNameToken("x4"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_SET)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Debug.Assert elem.getType = SqlTokenType.KEY_SET

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "set --c1"
    Debug.Assert strArr.Item(1) = "    x1 = x2,"
    Debug.Assert strArr.Item(2) = "    x3 = x4"

    ' ==============================
    ' 6. 扫描器内: select + 一个注释 + distinct + STR_NAME + [,] + STR_NAME
    '    测试: 解析 select
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getSelectToken, createShareLineCommentToken("--c1"), getDistinctToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_SELECT)
    ' 3. 检查结果
    Debug.Assert elem.getType = SqlTokenType.KEY_SELECT
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0)  = "select --c1"
    Debug.Assert strArr.Item(1)  = "    distinct"
    Debug.Assert strArr.Item(2)  = "    x1,"
    Debug.Assert strArr.Item(3)  = "    x2"

    ' ==============================
    ' 7. 扫描器内:
    '            ( +
    '            select + 一个注释 + distinct + STR_NAME + [,] + STR_NAME
    '            + )
    '    测试: 解析 ( 起始的作用域
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getLeftParenthesisToken, _
        _
        getSelectToken, createShareLineCommentToken("--c1"), getDistinctToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.LEFT_PARENTHESIS)
    ' 3. 检查结果
    Debug.Assert elem.getType = SqlTokenType.KEY_SELECT
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 6
    Debug.Assert strArr.Item(0)  = "("
    Debug.Assert strArr.Item(1)  = "    select --c1"
    Debug.Assert strArr.Item(2)  = "        distinct"
    Debug.Assert strArr.Item(3)  = "        x1,"
    Debug.Assert strArr.Item(4)  = "        x2"
    Debug.Assert strArr.Item(5)  = ")"

    ' ==============================
    ' 8. 扫描器内: on + DUPLICATE + KEY + UPDATE
    '            + c1 = 'abc',
    '            + c2 = 'xyz'
    '            + UNION
    '    测试: 解析 on DUPLICATE 作用域
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getOnToken, getDuplicateToken, getKeyToken, getUpdateToken, _
        getStrNameToken("c1"), getEqConditionOperator, createStrToken("abc"), getCommaToken, _
        getStrNameToken("c2"), getEqConditionOperator, createStrToken("xyz"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_ON)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterKeySectionEnv
    Debug.Assert elem.getType = SqlTokenType.KEY_DUPLICATE
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext()
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 3
    Debug.Assert strArr.Item(0) = "on duplicate key update"
    Debug.Assert strArr.Item(1) = "    c1 = 'abc',"
    Debug.Assert strArr.Item(2) = "    c2 = 'xyz'"

    ' ==============================
    ' 9. 扫描器内: on + STR_NAME
    '    测试: 无法解析 on 开头，但是不是 on DUPLICATE 的作用域
    ' ==============================
    ' 1. 创建一个扫描器
    Set scanner = getObjArrayRefScanner(getOnToken, getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = insertAnalyzer.ISqlFormatterSqlSubEnvAnalyzer_parseSubCompositionEnv(scanner, Nothing, SqlTokenType.KEY_ON)
    ' 3. 检查结果
    Debug.Assert elem Is Nothing
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext()
    Debug.Assert token.tokenType = SqlTokenType.KEY_ON
End Sub